#include "cpu/reg.h"
#include "string.h"
#include "debug.h"

#define NR_SREG	4

uint32_t seg_base(uint8_t sr);
uint32_t seg_limit(uint8_t sr);

lnaddr_t seg_translate(swaddr_t addr, size_t len, uint8_t sr) {
	if (cpu.PE == false) {
		return addr;
	}
	Assert(sr < NR_SREG, "undefined sreg number: %d\n", sr);
	Assert(addr + len < seg_limit(sr), "offset 0x%x is out of limit(0x%x) of segment %%%s\n", addr, seg_limit(sr), sregs[sr]);
	lnaddr_t lnaddr = addr + seg_base(sr);
	return lnaddr;
}

typedef uint8_t desctor[8];
desctor desc_cache[NR_SREG];

uint32_t seg_base(uint8_t sr) {
	uint32_t base;
	uint8_t *desc = &desc_cache[sr][0];
	base = desc[7];
	base = (base << 8) | desc[4];
	base = (base << 8) | desc[3];
	base = (base << 8) | desc[2];
	return base;
}

uint32_t seg_limit(uint8_t sr) {
	uint32_t limit;
	uint8_t *desc = &desc_cache[sr][0];
	limit = desc[6] & 0xf;
	limit = (limit << 8) | desc[1];
	limit = (limit << 8) | desc[0];
	if ((desc[6] & 0x80) != 0) {
		limit = (limit << 12) | 0xffff;
	}
	return limit;
}

uint32_t lnaddr_read(lnaddr_t, size_t);

void desc_add(uint8_t sr) {
	uint32_t index = sreg(sr) & ~7;
	index += (uint32_t)(cpu.gdtr >> 16);
	int i;
	for (i = 0; i < 8; i ++) {
		desc_cache[sr][i] = lnaddr_read(index + i, 1);
	}
}

void init_desc_cache() {
	uint8_t *desc = &desc_cache[S_CS][0];
	desc[7] = desc[4] = desc[3] = desc[2] = 0;
	desc[6] = 0xcf;
	desc[1] = desc[0] = 0xff;
}
